package com.googlecode.cryptoid;


public class Matrix {	
	
	public static void createUnit(){
		int size = 4;
		int[][] matrix= new int[size][size];
		for(int i=0 ; i < size; i++){
			for(int j=0 ; j < size; j++)
				matrix[i][j]=0;
		}
		for(int i=0 ; i < size; i++)
			matrix[i][i]=1;
		
		Matrix.name="new_matrix";
		Matrix.size = 2;
		Matrix.determinant = 1;
		
		Matrix.matrix = new int[size][size];
		for(int i=0 ; i < size; i++){
			for(int j=0 ; j < size; j++)
				Matrix.matrix[i][j]=matrix[i][j];
		}
		
		Matrix.inverse = new int[size][size];
		for(int i=0 ; i < size; i++){
			for(int j=0 ; j < size; j++)
				Matrix.inverse[i][j]=matrix[i][j];
		}
	}
	
	static String name;
	static int size;
	static int matrix[][];
	static int determinant;
	static int inverse[][];
	

	public static boolean initialize(){
		determinant = getDet(matrix);
		if (determinant % Main.encoding != 0){
			invertMatrix();
			return true;
		}
		else
			return false;
	}
	
	
	private static int getDet(int[][] mtx){
		int det = 0;
		int s = mtx[0].length;
		if( s > 2)
			for (int j = 0, k = 1 ; j < s; j++, k = -(k))
				det = det + k * mtx[0][j] * getDet(coMatrix(mtx, 0, j));
		else if(s == 2)
			det = mtx[0][0] * mtx[1][1] - mtx[0][1] * mtx[1][0];
		else if(s == 1)
			det = mtx[0][0];
		else
			det = 0;
			
		return det;
	}
	
	private static int[][] coMatrix(int[][] mtx, int i, int j){
		int s = mtx[0].length;
		int[][] coM = new int[s-1][s-1];
		int l = 0;
		for(int m = 0; m < s; m++ ){
			if( m != i){
				if(m<i)
					l = m;
				else if(m > i)
					l = m - 1;
				
				for(int n = 0; n < s; n++){
					if(n<j)
						coM[l][n] = mtx[m][n];
					else if(n > j)
						coM[l][n-1] = mtx[m][n];
				}
			}
		}
		
		return coM;
	}
	

	
	private static void invertMatrix(){
		int[][] candidate= new int[size][size];
		for(int i=0; i < size; i++){
			for(int j=0 ; j < size; j++)
				candidate[j][i]=(int) (Math.pow(-1,i+j)  * getDet(coMatrix(matrix, i, j)));
		}
		
		
		for(int i=0 ; i < size; i++){
			for(int j=0 ; j < size; j++){
				while(candidate[i][j] % determinant != 0)
					candidate[i][j] = candidate[i][j] + Main.encoding;
				inverse[i][j] = (candidate[i][j] / determinant) % Main.encoding;
			}
		}
	}
	
	public static void encrypt(){
		ViewMessage.encrypted = crypt(matrix, ViewMessage.raw);
	}
	
	public static void decrypt(){
		ViewMessage.raw = crypt(inverse, ViewMessage.encrypted);
	}
	
	
	
	private static String crypt(int[][] mtx, String str){
		int length = str.length();
		
		if(length == 0)
			return null;
		
		int inputLength = length;
		
		char[] input = str.toCharArray();
		while (length % size != 0 ) {
			length++;
		}
		
		int[] carrier = new int[length];
		char[] output = new char[length];
		
		for(int i = 0; i < length; i++)
			if(i < inputLength)
				carrier[i] = getIntValue(input[i]);
			else
				carrier[i] = getIntValue(' ');
		
		int[] factor = new int[size];
		int[] product = new int[size];
		
		for(int i = 0; i< length;){
			int k =i;
			for(int j=0; j < size; j++, k++)
				factor[j] = carrier[k];
			
			product = multiply(factor,mtx);
			
			for(int j=0; j < size; j++, i++){
				int chr = product[j] % Main.encoding;
				while (chr < 0)
					chr = chr + Main.encoding;
				output[i] = getChar(chr);
			}
		}
		
		return String.valueOf(output);
	
	}
	
	private static char getChar(int chr) {
		char c;
		
		switch(Main.encoding){
		case Main.ASCII:
			if (chr == 95)
				c = '\n';
			else if(chr == 96)
				c = (char) 169;
			else
				c = (char) (chr + 32);
			break;
		default:
			c = ' ';
		}
		return c;
	}


	private static int getIntValue(char c) {
		int chr;
		
		switch(Main.encoding){
		case Main.ASCII:
			if(c == '\n')
				chr = 95;
			else if(c == (char) 169)
				chr = 96;
			else
				chr = ((int)(c)) - 32;
			break;
		default:
			chr = 0;
		}
		return chr;
	}
	
	private static int[] multiply(int [] vtr, int[][] mtx){
		int[] product = new int[size];
		for(int i = 0; i < size; i++){
			product[i] = 0;
			for(int j = 0; j < size; j++)
				product[i] = product[i] + vtr[j] * mtx[i][j];
		}
		
		
		return product;
	}
	
}
